/******************************************************************************
 * QwtPolar Widget Library
 * Copyright (C) 2008   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include "qwt_polar_canvas.h"
#include "qwt_painter.h"
#include "qwt_polar_plot.h"
#include <QEvent>
#include <QPaintEvent>
#include <QPainter>
#include <QPixmap>
#include <QStyle>
#include <QStyleOption>

static inline void qwtDrawStyledBackground(QWidget *widget, QPainter *painter) {
  QStyleOption opt;
  opt.initFrom(widget);
  widget->style()->drawPrimitive(QStyle::PE_Widget, &opt, painter, widget);
}

static QWidget *qwtBackgroundWidget(QWidget *w) {
  if (w->parentWidget() == nullptr)
    return w;

  if (w->autoFillBackground()) {
    const QBrush brush = w->palette().brush(w->backgroundRole());
    if (brush.color().alpha() > 0)
      return w;
  }

  if (w->testAttribute(Qt::WA_StyledBackground)) {
    QImage image(1, 1, QImage::Format_ARGB32);
    image.fill(Qt::transparent);

    QPainter painter(&image);
    painter.translate(-w->rect().center());
    qwtDrawStyledBackground(w, &painter);
    painter.end();

    if (qAlpha(image.pixel(0, 0)) != 0)
      return w;
  }

  return qwtBackgroundWidget(w->parentWidget());
}

class QwtPolarCanvas::PrivateData {
public:
  PrivateData() : backingStore(nullptr) {}

  ~PrivateData() { delete backingStore; }

  QwtPolarCanvas::PaintAttributes paintAttributes;
  QPixmap *backingStore;
};

//! Constructor
QwtPolarCanvas::QwtPolarCanvas(QwtPolarPlot *plot) : QFrame(plot) {
  m_data = new PrivateData;

#ifndef QT_NO_CURSOR
  setCursor(Qt::CrossCursor);
#endif
  setFocusPolicy(Qt::WheelFocus);

  setPaintAttribute(BackingStore, true);
}

//! Destructor
QwtPolarCanvas::~QwtPolarCanvas() { delete m_data; }

//! \return Parent plot widget
QwtPolarPlot *QwtPolarCanvas::plot() {
  return qobject_cast<QwtPolarPlot *>(parent());
}

//! \return Parent plot widget
const QwtPolarPlot *QwtPolarCanvas::plot() const {
  return qobject_cast<QwtPolarPlot *>(parent());
}

/*!
   \brief Changing the paint attributes

   \param attribute Paint attribute
   \param on On/Off

   The default setting enables BackingStore

   \sa testPaintAttribute(), paintCache()
 */
void QwtPolarCanvas::setPaintAttribute(PaintAttribute attribute, bool on) {
  if (bool(m_data->paintAttributes & attribute) == on)
    return;

  if (on)
    m_data->paintAttributes |= attribute;
  else
    m_data->paintAttributes &= ~attribute;

  switch (attribute) {
  case BackingStore: {
    if (on) {
      if (m_data->backingStore == nullptr)
        m_data->backingStore = new QPixmap();

      if (isVisible()) {
        const QRect cr = contentsRect();
#if QT_VERSION >= 0x050000
        *m_data->backingStore = grab(cr);
#else
        *m_data->backingStore = QPixmap::grabWidget(this, cr);
#endif
      }
    } else {
      delete m_data->backingStore;
      m_data->backingStore = nullptr;
    }
    break;
  }
  }
}

/*!
   Test whether a paint attribute is enabled

   \param attribute Paint attribute
   \return true if the attribute is enabled
   \sa setPaintAttribute()
 */
bool QwtPolarCanvas::testPaintAttribute(PaintAttribute attribute) const {
  return (m_data->paintAttributes & attribute) != 0;
}

//! \return Backing store, might be null
const QPixmap *QwtPolarCanvas::backingStore() const {
  return m_data->backingStore;
}

//! Invalidate the internal backing store
void QwtPolarCanvas::invalidateBackingStore() {
  if (m_data->backingStore)
    *m_data->backingStore = QPixmap();
}

/*!
   Paint event
   \param event Paint event
 */
void QwtPolarCanvas::paintEvent(QPaintEvent *event) {
  QPainter painter(this);
  painter.setClipRegion(event->region());

  if ((m_data->paintAttributes & BackingStore) &&
      m_data->backingStore != nullptr) {
    QPixmap &bs = *m_data->backingStore;
    if (bs.size() != size()) {
      bs = QPixmap(size());
#ifdef Q_WS_X11
      if (bs.x11Info().screen() != x11Info().screen())
        bs.x11SetScreen(x11Info().screen());
#endif

      QPainter p;

      if (testAttribute(Qt::WA_StyledBackground)) {
        p.begin(&bs);
        qwtDrawStyledBackground(this, &p);
      } else {
        if (autoFillBackground()) {
          p.begin(&bs);
          p.fillRect(rect(), palette().brush(backgroundRole()));
        } else {
          QWidget *bgWidget = qwtBackgroundWidget(plot());

          QwtPainter::fillPixmap(bgWidget, bs,
                                 mapTo(bgWidget, rect().topLeft()));

          p.begin(&bs);
        }
      }

      plot()->drawCanvas(&p, contentsRect());

      if (frameWidth() > 0)
        drawFrame(&p);
    }

    painter.drawPixmap(0, 0, *m_data->backingStore);
  } else {
    qwtDrawStyledBackground(this, &painter);

    plot()->drawCanvas(&painter, contentsRect());

    if (frameWidth() > 0)
      drawFrame(&painter);
  }
}

/*!
   Resize event
   \param event Resize event
 */
void QwtPolarCanvas::resizeEvent(QResizeEvent *event) {
  QFrame::resizeEvent(event);

  for (int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++)
    plot()->updateScale(scaleId);
}

/*!
    Translate a point from widget into plot coordinates

    \param pos Point in widget coordinates of the plot canvas
    \return Point in plot coordinates

    \sa transform()
 */
QwtPointPolar QwtPolarCanvas::invTransform(const QPoint &pos) const {
  const QwtPolarPlot *pl = plot();

  const QwtScaleMap azimuthMap = pl->scaleMap(QwtPolar::Azimuth);
  const QwtScaleMap radialMap = pl->scaleMap(QwtPolar::Radius);

  const QPointF center = pl->plotRect().center();

  double dx = pos.x() - center.x();
  double dy = -(pos.y() - center.y());

  const QwtPointPolar polarPos = QwtPointPolar(QPointF(dx, dy)).normalized();

  double azimuth = azimuthMap.invTransform(polarPos.azimuth());

  // normalize the azimuth
  double min = azimuthMap.s1();
  double max = azimuthMap.s2();
  if (max < min)
    qSwap(min, max);

  if (azimuth < min) {
    azimuth += max - min;
  } else if (azimuth > max) {
    azimuth -= max - min;
  }

  const double radius = radialMap.invTransform(polarPos.radius());

  return QwtPointPolar(azimuth, radius);
}

/*!
    Translate a point from plot into widget coordinates

    \param polarPos Point in plot coordinates
    \return Point in widget coordinates
    \sa transform()
 */
QPoint QwtPolarCanvas::transform(const QwtPointPolar &polarPos) const {
  const QwtPolarPlot *pl = plot();

  const QwtScaleMap azimuthMap = pl->scaleMap(QwtPolar::Azimuth);
  const QwtScaleMap radialMap = pl->scaleMap(QwtPolar::Radius);

  const double radius = radialMap.transform(polarPos.radius());
  const double azimuth = azimuthMap.transform(polarPos.azimuth());

  const QPointF pos = qwtPolar2Pos(pl->plotRect().center(), radius, azimuth);

  return pos.toPoint();
}
